<%@ page language="java" contentType="text/html; charset=UTF8"
	pageEncoding="UTF8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<meta name="categories" content="数据库">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>预编译SQL的缺陷</title>
</head>
<body>
	<div class="article-wrapper">
		<article>
			<h1 class="title">预编译SQL的缺陷</h1>
			<hr>
			<section>
				<p>
					众所周知，JDBC的PrepareStatement接口可以进行预编译SQL。这样数据库会将编译后的SQL绑定在这个连接上，只需要不断的进行参数替换就可以提高SQL的执行效率。但是这样的设计是有缺陷的，并不是所有的情况都会提高执行效率，反而也会有最差的情况。
				</p>
				<p>
					废话不多说。因为数据库的预编译一但创建，执行计划就会一成不变的被保存起来（预编译SQL）。因此，他不可能去适应所有的情况。比如说下面的SQL：
				</p>
				<code> SELECT * FROM USER WHERE LOCATION = '广西' </code>
				<p>
					假如说，USER表中存储的数据中，“地域”这个字段分配的很广，也就是说他的取值区间平均来说很广。数据库会认为，索引的选择性较好。（PS：何为选择性？假如说性别，一万条数据里只有两种可能，那么他的选择性就不好。因为，索引的B+树只会存在两个结点，数据太过于聚集）所以，这条执行计划就会利用索引。假如说，来自“广西”的USER很少，那么确实取出速度会很快。假如说这条SQL：
				</p>
				<code> SELECT * FROM USER WHERE LOCATION = '上海' </code>
				<p>
					因为在上海的USER特别多，假如说有上千条记录，那么返回的数据特别多，就回产生很多随机读取。因为，LOCATION不是主键，数据库是按照主键进行聚簇的。这时候，用索引就会相当慢。为什么？
				</p>
			</section>
			<section>
				<p>
					因为在MySQL中，如果走索引，就会访问两次硬盘。因为，索引的B+树中存储的DATA并不是表数据的集邮物理位置（RowID），而是主键！他会拿着主键再去磁盘上寻找真实的数据。为什么要这样？因为假如索引有10000个，那么更改主键的话，那么这些数据就不得不搬到合适的物理位置。这样，就会更改10000处！但是并不是所有的数据库都是这么做的，比如Oracle。ORACLE索引中的数据不仅有主键还有集邮物理位置（RowID），这样做的想法是，住建部可能经常更换。他先拿物理位置（RowID）去取出真实数据，然后和主键比较一下，如果不对再拿主键去取，然后更新中的物理位置（RowID）。这样做的话，就会出现最好情况和最差情况了。
				</p>
				<p>所以数据量不大的时候，还不如全表扫描来得快。</p>
			</section>
		</article>
	</div>
</body>
</html>
